package net.xo68.destiny.core.timingwheel.impl;

import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.core.timingwheel.Timeout;
import net.xo68.destiny.core.timingwheel.TimingWheelBucket;
import net.xo68.destiny.core.timingwheel.TimingWheel;
import net.xo68.destiny.core.timingwheel.process.TimeoutEventProcessor;
import net.xo68.destiny.core.timingwheel.process.TimeoutEventProcessorImpl;

import java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * 时光轮超时项（封装定时任务）
 * @author wuxie
 * @version 2020-10-18
 */
@Slf4j
public class TimingWheelTimeout<T extends Serializable> implements Timeout<T> {

    public static final int ST_INIT       = 0;
    public static final int ST_CANCELLED  = 1;
    public static final int ST_EXPIRED    = 2;

    public static final AtomicIntegerFieldUpdater<TimingWheelTimeout> STATE_UPDATER = AtomicIntegerFieldUpdater
            .newUpdater(
                    TimingWheelTimeout.class,
                    "state");
    /**
     * 状态
     */
    @SuppressWarnings({ "unused", "FieldMayBeFinal", "RedundantFieldInitialization" })
    private volatile int state= ST_INIT;

    /**
     * 所属时光轮调度
     */
    private final TimingWheel<T> timingWheel;

    /**
     * 任务序列号
     */
    private final String taskSequence;

    /**
     * 时光轮桶
     */
    private TimingWheelBucket<T> bucket;

    /**
     * 原延迟时间，单位毫秒
     */
    private final long sourceDeadline;
    /**
     * 延迟时间
     *  triggerTime - startTime
     * （触发时间-时光轮启动时间）
     */
    private final long deadline;
    /**
     * 剩余的轮数
     */
    private long remainingRounds;

    /**
     * 触发参数
     */
    private final T triggerParameter;



    public TimingWheelTimeout(String taskSequence, TimingWheel<T> timingWheel, long sourceDeadline, long deadline, T triggerParameter) {
        this.taskSequence=taskSequence;
        this.timingWheel = timingWheel;
        this.sourceDeadline=sourceDeadline;
        this.deadline=deadline;
        this.triggerParameter=triggerParameter;
    }



    @Override
    public boolean isExpired(){
        return state() == ST_CANCELLED;
    }
    @Override
    public boolean isCancelled() {
        return state() == ST_CANCELLED;
    }

    /**
     * 比对并设置为新值
     * @param expected 比对值
     * @param state 新值
     * @return
     */
    public boolean compareAndSetState(int expected, int state) {
        return STATE_UPDATER.compareAndSet(this, expected, state);
    }

    /**
     * 触发过期，执行任务
     */
    @Override
    public void expire() {
        if (!compareAndSetState(ST_INIT, ST_EXPIRED)) {
            return;
        }
        try {
            //执行任务
            TimeoutEventProcessor<T> eventProcessor= new TimeoutEventProcessorImpl<>(this.timingWheel,this);
            this.timingWheel.getTaskExecutor().submit(eventProcessor);
            //log.debug("**** 执行任务：{},tick:{}",this.taskSequence(), this.timingWheel.getTick().get());
            //this.t
        } catch (Throwable t) {
            if (log.isWarnEnabled()) {
                log.warn("An exception was thrown by " + this.taskSequence + '.', t);
            }
        }
    }

    /**
     * 任务处理结束后，从缓冲中移除
     */
    @Override
    public void remove(){
        TimingWheelBucket<T> bucket=this.bucket;
        if(bucket!=null){
            bucket.remove(this);
        }
        timingWheel.getHashedBuffer().remove(this.taskSequence());
        timingWheel.getPendingTimeouts().decrementAndGet();
    }

    @Override
    public boolean cancel() {
        // only update the state it will be removed from HashedWheelBucket on next tick.
        if (!compareAndSetState(ST_INIT, ST_CANCELLED)) {
            return false;
        }
        // If a task should be canceled we put this to another queue which will be processed on each tick.
        // So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way
        // we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible.
        timingWheel.getCancelledTimeouts().add(this);
        return true;
    }


    @Override
    public String taskSequence() {
        return taskSequence;
    }

    @Override
    public TimingWheelBucket<T> bucket() {
        return bucket;
    }

    @Override
    public void bucket(TimingWheelBucket<T> bucket) {
        this.bucket = bucket;
    }

    @Override
    public long sourceDeadline(){
        return sourceDeadline;
    }
    @Override
    public long deadline() {
        return deadline;
    }

    @Override
    public long remainingRounds() {
        return remainingRounds;
    }

    @Override
    public void remainingRounds(long remainingRounds) {
        this.remainingRounds = remainingRounds;
    }

    /**
     * 剩余轮数减少1
     */
    @Override
    public void remainingRoundDecrement() {
        this.remainingRounds--;
    }

    @Override
    public T triggerParameter() {
        return triggerParameter;
    }

    @Override
    public int state() {
        return state;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TimingWheelTimeout<?> timeout = (TimingWheelTimeout<?>) o;
        return taskSequence.equals(timeout.taskSequence);
    }

    @Override
    public int hashCode() {
        return Objects.hash(taskSequence);
    }

    @Override
    public String toString() {
        return "TimingWheelTimeout{" +
                "state=" + state +
                ", taskSequence='" + taskSequence + '\'' +
                ", sourceDeadline=" + sourceDeadline +
                ", deadline=" + deadline +
                ", remainingRounds=" + remainingRounds +
                ", triggerParameter=" + triggerParameter +
                '}';
    }
}
